package gu.simplemq.jms;

import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.jms.Connection;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;

import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

/**
 * {@link ExceptionListener}容器实现，支持多个{@link ExceptionListener}实例
 * @author guyadong
 * @since 2.3.8
 */
class ExceptionListenerContainer implements ExceptionListener,JmsConstants{
	private static final ExecutorService executor = MoreExecutors.getExitingExecutorService(
			new ThreadPoolExecutor(1, 1,
	                0L, TimeUnit.SECONDS,
	                new LinkedBlockingQueue<Runnable>(),
	                new ThreadFactoryBuilder().setNameFormat("QpidJMS-ExceptionListener-%d").build()));

	private final CopyOnWriteArraySet<ExceptionListener> listeners= new CopyOnWriteArraySet<>();
	ExceptionListenerContainer(ExceptionListener listener) {
		merge(listener);
	}

	@Override
	public void onException(final JMSException exception) {
		for(final ExceptionListener listener:listeners) {
			if(null != listener) {
                executor.submit(new Runnable() {
                    @Override
                    public void run() {
                    	listener.onException(exception);
                    }
                });
			}
		}
	}
	/**
	 * 当{@code listener}对象合并到当前对象,
	 * 如果{@code listener}为{@link ExceptionListenerContainer}实例，
	 * 则将{@code listener}的{@link #listeners}的元素合并到当前对象的{@link #listeners}字段，
	 * 否则将{@code listener}添加到{@link #listeners}中
	 * @param listener
	 * @return 当前对象
	 */
	private ExceptionListenerContainer merge(ExceptionListener listener) {
		if(listener instanceof ExceptionListenerContainer) {
			ExceptionListenerContainer container = (ExceptionListenerContainer)listener;
			listeners.addAll(container.listeners);
		}else if(null != listener) {
			listeners.add(listener);
		}
		return this;
	}
	/**
	 * 将当前实例设置为{@link Connection}的异常侦听器,
	 * 如果连接对象关闭则忽略，输出错误日志
	 * @param connection
	 * @see Connection#setExceptionListener(ExceptionListener)
	 */
	void bind(Connection connection) {
		if(null != connection) {
			try {
				ExceptionListener old = connection.getExceptionListener();
				if(old instanceof ExceptionListenerContainer) {
					((ExceptionListenerContainer)old).merge(this);
				}else {
					connection.setExceptionListener(merge(old));
				}
			} catch (JMSException e) {
				logger.error(e.getMessage());
			}
		}
	}
}
